#pragma once

#include <string>
#include <sys/socket.h>
#include <sys/uio.h>

class Buffer
{
public:
    Buffer(int capacity = 0)
        : _capacity(capacity),
          _data(capacity, '0')
    {
    }
    void ExpandRoom(int writesize)
    {
        // |__|___|___|
        // 0  r   w  cap
        int rightWriteRoom = _capacity - _wpos;
        int leftWriteRoom = _rpos;
        // 1. 内存够用
        if (writesize <= rightWriteRoom)
            return;
        // 2. 内存需要合并才够用 交换 r-w 写入 0-(w-r)
        //    writesize > rightWriteRoom
        if (leftWriteRoom + rightWriteRoom >= writesize)
        {
            int w = 0;
            for (int r = _rpos; r < _wpos; ++r)
            {
                _data[w++] = _data[r];
            }
            _rpos = 0;
            _wpos = w;
            return;
        }
        // 3. 内存不够用，需要扩容
        _capacity += writesize;
        _data.resize(_capacity, '0');
    }
    void AppendData(const std::string &buf)
    {
        // 检测容量
        ExpandRoom(buf.size());
        // std::cout << "capacity: " << _capacity << std::endl;
        //   写
        //   careful
        buf.copy(const_cast<char *>(_data.c_str()) + _wpos, buf.size(), 0);
        _wpos += buf.size();
        // std::cout << "wpos: " << _wpos << std::endl;
        // std::cout << "rpos: " << _rpos << std::endl;
    }
    void AppendData(const char *buf, int size)
    {
        // 检测容量
        ExpandRoom(size);
        // std::cout << "capacity: " << _capacity << std::endl;
        //  写
        for (int i = 0; i < size; ++i)
        {
            _data[_wpos++] = buf[i];
        }
    }
    int ReadSocket(int fd)
    {
        // LOG(INFO) << fd << std::endl;
        //  readv
        iovec vec[2];
        int writeable = WriteAbleSize();
        // 初始化vec
        vec[0].iov_base = const_cast<char *>(_data.c_str()) + _wpos;
        vec[0].iov_len = writeable;
#define BUF_SIZE 40960
        char *buffer = new char[BUF_SIZE];
        vec[1].iov_base = buffer;
        vec[1].iov_len = BUF_SIZE;
        ssize_t sz = readv(fd, vec, 2);
        // LOG(INFO) << "sz\n";
        // LOG(INFO) << (const_cast<char *>(_data.c_str()) + _rpos) << std::endl;
        // LOG(INFO) << "end" << std::endl;
        if (sz <= 0)
        {
            delete[] buffer;
            return sz;
        }
        else if (sz <= writeable)
        {
            _wpos += sz;
        }
        else
        {
            _wpos = _capacity;
            AppendData(buffer);
        }
        delete[] buffer;
        return sz;
    }
    int SendData(int fd)
    {
        int readAble = ReadAbleSize();
        int total = 0;
        // for (int i = _rpos; i < _rpos + readAble; ++i)
        // {
        //     std::cout << _data[i];
        // }
        // std::cout << "readable:" << readAble << std::endl;
        while (total < readAble)
        {
            ssize_t sz = write(fd, const_cast<char *>(_data.c_str()) + _rpos, readAble - total);
            // std::cout << "sz:" << sz << std::endl;
            if (sz < 0)
            {
                if (errno == EAGAIN)
                {
                    usleep(20);
                    continue;
                }
                else
                {
                    // 读端关闭
                    _rpos = _wpos;
                    return -1;
                }
            }
            total += sz;
            _rpos += sz;
            // 休眠10us 不发太快
            usleep(10);
        }
        // std::cout << "total:" << total << std::endl;
        return total;
    }
    std::string ReadLine()
    {
        size_t pos = _data.find("\r\n", _rpos);
        if (pos == std::string::npos)
            return "";
        std::string line = _data.substr(_rpos, pos - _rpos);
        _rpos = pos + 2;
        return move(line);
    }
    int ReadAbleSize()
    {
        return _wpos - _rpos;
    }
    int WriteAbleSize()
    {
        return _capacity - _wpos;
    }
    int Capacity()
    {
        return _capacity;
    }

private:
    std::string _data; // 数据块
    int _rpos = 0;
    int _wpos = 0;
    int _capacity = 0;
};